package solver

import (
	"log"
)

type Node struct {
	cell                 *Cell
	cand1, cand2         int
	chosenCand, linkCand int
	linked               bool
}

func (g *Grid) chooseBiValueNode() []*Node {
	nodes := []*Node{}
	for _, row := range g.rows {
		for _, cell := range row.cells {
			if cell.candCount != 2 {
				continue
			}
			cands := intsFromBools(cell.cands)
			nodes = append(nodes, &Node{cell: cell,
				cand1: cands[0],
				cand2: cands[1]})
		}
	}
	return nodes
}

func (g *Grid) verifyXYChain(node1, node2 *Node, nodes []*Node) (used bool) {
	cands1 := intsFromBools(node1.cell.cands)
	cands2 := intsFromBools(node2.cell.cands)
	icands := intersect(cands1, cands2)
	if len(icands) != 1 {
		return false
	}
	seenCells1 := g.seenCells(node1.cell)
	seenCells2 := g.seenCells(node2.cell)

	iCells := shareCells(seenCells1, seenCells2)
	canRemoved := false
	for _, cell := range iCells {
		if cell.cands[icands[0]] {
			canRemoved = true
			break
		}
	}
	if !canRemoved {
		return false
	}

	for _, node := range nodes {
		node.linked = false
	}

	chosenCand := icands[0]
	node1.chosenCand = difference(cands1, icands)[0]
	node1.linkCand = chosenCand

	return findChain(chosenCand, node1, node2, nodes)
}

func findChain(chosenCand int, node, end *Node, nodes []*Node) bool {
	if node == nil {
		return false
	}
	if node.linked {
		return false
	} else {
		node.linked = true
	}

	//fmt.Printf("(%v, %v) chosen=%v link=%v\n", node.cell.rowNum, node.cell.colNum, node.chosenCand+1, node.linkCand+1)
	if equalCells(node.cell, end.cell) && node.chosenCand == chosenCand {
		//fmt.Println("found chain")
		return true
	}

	seenNodes := findSeenNodes(node, nodes)
	for _, seenNode := range seenNodes {
		if findChain(chosenCand, seenNode, end, nodes) {
			return true
		}
	}

	return false

}

func findSeenNodes(from *Node, nodes []*Node) []*Node {
	seenNodes := []*Node{}
	for _, node := range nodes {
		if from.cell.rowNum != node.cell.rowNum && from.cell.colNum != node.cell.colNum && from.cell.boxNum != node.cell.boxNum {
			continue
		}
		if node.linked {
			continue
		}
		if from.chosenCand == node.cand1 {
			node.linkCand = from.chosenCand
			node.chosenCand = node.cand2
		} else if from.chosenCand == node.cand2 {
			node.linkCand = from.chosenCand
			node.chosenCand = node.cand1
		} else {
			continue
		}
		seenNodes = append(seenNodes, node)
	}
	return seenNodes
}
func (g *Grid) XYChain() (used bool) {
	nodes := g.chooseBiValueNode()
	n := len(nodes)
	for i := 0; i < n-1; i++ {
		for j := i + 1; j < n; j++ {
			if !g.verifyXYChain(nodes[i], nodes[j], nodes) {
				continue
			}
			if g.clearupSeenFromCells(nodes[i].linkCand,
				[]*Cell{nodes[i].cell, nodes[j].cell}) {
				used = true
				log.Printf("r%vc%v, r%vc%v %v XYChain",
					nodes[i].cell.rowNum, nodes[i].cell.colNum,
					nodes[j].cell.rowNum, nodes[j].cell.colNum,
					nodes[i].linkCand+1)
				return used
			}
		}
	}
	return
}
